import 'dart:async';
import 'dart:io';
import 'dart:convert';

import '../pd_log.dart';
import 'file_events.dart';
// LogLedger 与 PDLogFile 由 pd_log.dart 再导出，可通过上方 import 访问。

/// 纯 Dart 的文件写入器，实现缓冲 + 定时/阈值刷盘到当天日志文件。
///
/// 路径结构采用 `<root>/<yyyy>/<MM>/<dd>.log`，其中 `<root>` 来自
/// `PDLog.logRootPath()` 的平台实现（例如 iOS/macOS 为 `ApplicationSupport/pd_log`）。
class DartFileLogger {
  DartFileLogger._internal();
  static final DartFileLogger instance = DartFileLogger._internal();

  bool _enabled = false;
  int _flushIntervalMs = 2000;
  int _maxBufferEntries = 100;
  int _maxBufferBytes = 64 * 1024;

  final List<List<int>> _buffer = <List<int>>[];
  int _bufferBytes = 0;
  Timer? _timer;
  bool _flushing = false;

  /// 配置写入器选项：
  /// - `enabled`：是否启用写入；关闭时会尝试立即刷盘以避免数据滞留；
  /// - `flushIntervalMs`：定时刷盘间隔；
  /// - `maxBufferEntries`：内存缓冲的最大条目数；
  /// - `maxBufferBytes`：内存缓冲的最大字节数。
  void configure({
    required bool enabled,
    int? flushIntervalMs,
    int? maxBufferEntries,
    int? maxBufferBytes,
  }) {
    _enabled = enabled;
    if (flushIntervalMs != null) _flushIntervalMs = flushIntervalMs;
    if (maxBufferEntries != null) _maxBufferEntries = maxBufferEntries;
    if (maxBufferBytes != null) _maxBufferBytes = maxBufferBytes;
    if (!_enabled) {
      // 关闭时尝试立即刷盘，避免丢数据
      unawaited(flushNow());
    }
  }

  /// 入队一条已格式化文本（应包含尾随换行）。
  ///
  /// 行为：
  /// - `enabled=false` 或空字符串将被忽略；
  /// - 写入内存缓冲并累计字节数；
  /// - 如达到条目/字节阈值则立即刷盘，否则按定时器调度。
  void enqueue(String text) {
    if (!_enabled || text.isEmpty) return;
    final bytes = utf8.encode(text);
    _buffer.add(bytes);
    _bufferBytes += bytes.length;
    // 条目或字节阈值达到则立刻刷盘；否则按定时器调度
    if (_buffer.length >= _maxBufferEntries ||
        _bufferBytes >= _maxBufferBytes) {
      _scheduleFlush(immediate: true);
    } else if (_timer == null) {
      _scheduleFlush(immediate: false);
    }
  }

  /// 主动立即刷盘（异步）。
  ///
  /// 将当前缓冲批量写入当天日志文件。若根路径不可用，
  /// 本次刷盘将被丢弃，以避免阻塞调用方。
  Future<void> flushNow() async {
    return _flushBuffer();
  }

  /// 调度刷盘：
  /// - `immediate=true` 立即触发；
  /// - 否则按 `_flushIntervalMs` 延时触发（去重同一时间的多个调度）。
  void _scheduleFlush({required bool immediate}) {
    _timer?.cancel();
    if (immediate) {
      unawaited(_flushBuffer());
      return;
    }
    _timer = Timer(Duration(milliseconds: _flushIntervalMs), () {
      unawaited(_flushBuffer());
    });
  }

  /// 实际执行刷盘：
  /// - 简单并发保护避免重入；
  /// - 原子性地从缓冲复制出待写入块，清空缓冲；
  /// - 追加写入到 `<root>/<yyyy>/<MM>/<dd>.log`；
  /// - 写入后广播文件事件并同步账本快照。
  Future<void> _flushBuffer() async {
    if (_flushing) return; // 简单并发保护
    _flushing = true;
    List<List<int>> chunks = const [];
    if (_buffer.isEmpty) {
      _flushing = false;
      _timer = null;
      return;
    }
    chunks = List<List<int>>.from(_buffer);
    _buffer.clear();
    _bufferBytes = 0;
    _timer = null;

    try {
      final filePath = await _currentLogFilePath();
      if (filePath == null || filePath.isEmpty) {
        // 根路径不可用，丢弃本次写入，避免阻塞
        _flushing = false;
        return;
      }

      final file = File(filePath);
      // 确保目录存在
      final dir = file.parent;
      if (!dir.existsSync()) {
        dir.createSync(recursive: true);
      }

      final existedBefore = file.existsSync();
      // 直接以追加模式写入
      final sink = file.openWrite(mode: FileMode.writeOnlyAppend);
      for (final c in chunks) {
        sink.add(c);
      }
      await sink.flush();
      await sink.close();

      // 广播 Dart 侧事件（去重由总线控制）；同时更新快照以保持最新尺寸/时间。
      try {
        final stat = file.statSync();
        final size = stat.size;
        final mod = stat.modified.millisecondsSinceEpoch;
        if (!existedBefore) {
          await LogLedger.recordCreated(file.path,
              sizeBytes: size, modifiedMs: mod);
          FileEventBus.instance
              .emitCreated(file.path, sizeBytes: size, modifiedMs: mod);
        } else {
          await LogLedger.syncFromFiles([
            PDLogFile(path: file.path, sizeBytes: size, modifiedMs: mod),
          ]);
          FileEventBus.instance
              .emitModified(file.path, sizeBytes: size, modifiedMs: mod);
        }
      } catch (_) {
        // ignore stat errors
      }
    } catch (_) {
      // 写入失败不重试，避免无限循环；后续新日志会再次尝试
    } finally {
      _flushing = false;
    }
  }

  /// 计算当天日志文件绝对路径：
  /// 返回 `<root>/<yyyy>/<MM>/<dd>.log`；
  /// 当 `root` 不可用时返回 `null`，调用方应优雅降级。
  Future<String?> _currentLogFilePath() async {
    final root = await PDLog.logRootPath();
    if (root == null || root.isEmpty) return null;
    final now = DateTime.now();
    final year = now.year.toString().padLeft(4, '0');
    final month = now.month.toString().padLeft(2, '0');
    final day = now.day.toString().padLeft(2, '0');
    return _joinAll([root, year, month, '$day.log']);
  }

  /// 路径拼接工具（容错处理空片段与分隔符）。
  static String _joinAll(List<String> parts) {
    String out = '';
    for (final p in parts) {
      if (p.isEmpty) continue;
      if (out.isEmpty) {
        out = p;
      } else {
        if (out.endsWith(Platform.pathSeparator)) {
          out = out + p;
        } else {
          out = out + Platform.pathSeparator + p;
        }
      }
    }
    return out;
  }
}
